package com.becca.cake.core.print;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.logger.ConsoleLogger;
import org.apache.avalon.framework.logger.Logger;
import org.krysalis.barcode4j.BarcodeException;
import org.krysalis.barcode4j.BarcodeGenerator;
import org.krysalis.barcode4j.BarcodeUtil;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
import org.krysalis.barcode4j.output.eps.EPSCanvasProvider;
import org.krysalis.barcode4j.output.svg.SVGCanvasProvider;
import org.krysalis.barcode4j.tools.MimeTypes;

/**
 * 一维码生成器
 * @copyright  尊星(上海)信息科技有限公司
 * @author hejinguo
 * @date 2016年6月20日 下午10:33:38
 */
public class BarcodeServlet extends HttpServlet {
    private static final long  serialVersionUID               = -1612710758060435089L;
    /**
     * url主要参数
     */
    public static final String BARCODE_MSG                    = "msg";
    public static final String BARCODE_TYPE                   = "type";
    public static final String BARCODE_HEIGHT                 = "height";
    public static final String BARCODE_MODULE_WIDTH           = "mw";                       //条码模块宽度
    public static final String BARCODE_WIDE_FACTOR            = "wf";                       //条码宽因子
    public static final String BARCODE_QUIET_ZONE             = "qz";                       //条码空白区
    public static final String BARCODE_HUMAN_READABLE_POS     = "hrp";                      //条码人类可读的POS
    public static final String BARCODE_FORMAT                 = "fmt";                      //条码格式
    public static final String BARCODE_IMAGE_RESOLUTION       = "res";                      //条码图像分辨率
    public static final String BARCODE_IMAGE_GRAYSCALE        = "gray";                     //条码图像灰度
    public static final String BARCODE_HUMAN_READABLE_SIZE    = "hrsize";                   //条码人类可读的尺寸
    public static final String BARCODE_HUMAN_READABLE_FONT    = "hrfont";                   //人类可读的条码字体
    public static final String BARCODE_HUMAN_READABLE_PATTERN = "hrpattern";                //条码可读格局
    private transient Logger   log                            = new ConsoleLogger(
                                                                  ConsoleLogger.LEVEL_INFO);

    /**
     * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        ByteArrayOutputStream bout = null;
        String format = determineFormat(request);
        try {
            int orientation = 0;
            Configuration cfg = buildCfg(request);
            String msg = request.getParameter(BARCODE_MSG);
            BarcodeUtil util = BarcodeUtil.getInstance();
            BarcodeGenerator gen;
            gen = util.createBarcodeGenerator(cfg);
            bout = new ByteArrayOutputStream(4096);
            //  try {
            if (format.equals(MimeTypes.MIME_SVG)) {
                //创建条码并使其向SVG
                SVGCanvasProvider svg = new SVGCanvasProvider(false, orientation);
                gen.generateBarcode(svg, msg);
                org.w3c.dom.DocumentFragment frag = svg.getDOMFragment();
                //SVG条形码序列
                TransformerFactory factory = TransformerFactory.newInstance();
                Transformer trans = factory.newTransformer();
                Source src = new DOMSource(frag);
                Result res = new StreamResult(bout);
                trans.transform(src, res);
            } else if (format.equals(MimeTypes.MIME_EPS)) {
                EPSCanvasProvider eps = new EPSCanvasProvider(bout, orientation);
                gen.generateBarcode(eps, msg);
                eps.finish();
            } else {
                String resText = request.getParameter(BARCODE_IMAGE_RESOLUTION);
                int resolution = 300;
                if (resText != null) {
                    resolution = Integer.parseInt(resText);
                }
                if (resolution > 2400) {
                    throw new IllegalArgumentException("Resolutions above 2400dpi are not allowed");
                }
                if (resolution < 10) {
                    throw new IllegalArgumentException("Minimum resolution must be 10dpi");
                }
                String gray = request.getParameter(BARCODE_IMAGE_GRAYSCALE);
                BitmapCanvasProvider bitmap = ("true".equalsIgnoreCase(gray) ? new BitmapCanvasProvider(
                    bout, format, resolution, BufferedImage.TYPE_BYTE_GRAY, true, orientation)
                    : new BitmapCanvasProvider(bout, format, resolution,
                        BufferedImage.TYPE_BYTE_BINARY, false, orientation));
                gen.generateBarcode(bitmap, msg);
                bitmap.finish();
            }
        } catch (ConfigurationException e) {
            log.error("Error while generating barcode", e);
        } catch (BarcodeException e) {
            log.error("Error while generating barcode", e);
        } catch (TransformerConfigurationException e) {
            log.error("Error while generating barcode", e);
        } catch (TransformerException e) {
            log.error("Error while generating barcode", e);
        } catch (IOException e) {
            log.error("Error while generating barcode IO流异常", e);
        } finally {
            try {
                bout.close();
            } catch (IOException e) {
                log.error("Error while generating barcode IO流异常", e);
            }
        }
        response.setContentType(format);
        response.setContentLength(bout.size());
        try {
            response.getOutputStream().write(bout.toByteArray());
            response.getOutputStream().flush();
        } catch (IOException e) {
            log.error("", e);
        }
    }

    /**
     * 检查为所需的输出格式要求
     * @param request the request to use
     * @return MIME type of the desired output format.
     */
    protected String determineFormat(HttpServletRequest request) {
        String format = request.getParameter(BARCODE_FORMAT);
        format = MimeTypes.expandFormat(format);
        if (format == null) {
            //默认图片显示格式改成jpeg,因为不是所有的浏览器都支持svg格式.
            format = MimeTypes.MIME_JPEG;
        }
        return format;
    }

    /**
     * 从请求建立一个Avalon配置对象
     * @param request the request to use
     * @return the newly built COnfiguration object
     * @todo Change to bean API
     */
    protected Configuration buildCfg(HttpServletRequest request) {
        DefaultConfiguration cfg = new DefaultConfiguration("barcode");
        //得到type
        String type = request.getParameter(BARCODE_TYPE);
        if (type == null) {
            type = "code128";
        }
        DefaultConfiguration child = new DefaultConfiguration(type);
        cfg.addChild(child);
        //获得附加属性
        DefaultConfiguration attr;
        attr = new DefaultConfiguration("height");
        attr.setValue(40);
        child.addChild(attr);
        attr = new DefaultConfiguration("module-width");
        attr.setValue("0.8");
        child.addChild(attr);
        String wideFactor = request.getParameter(BARCODE_WIDE_FACTOR) != null ? request
            .getParameter(BARCODE_WIDE_FACTOR) : "40";
        if (wideFactor != null) {
            attr = new DefaultConfiguration("wide-factor");
            attr.setValue(wideFactor);
            child.addChild(attr);
        }
        String quietZone = request.getParameter(BARCODE_QUIET_ZONE);
        if (quietZone != null) {
            attr = new DefaultConfiguration("quiet-zone");
            if (quietZone.startsWith("disable")) {
                attr.setAttribute("enabled", "false");
            } else {
                attr.setValue(quietZone);
            }
            child.addChild(attr);
        }
        // 创造可读的配置，根据新的条码元素映射
        // 在人类可读的字体名称，字体大小，位置
        // 自定义图案.
        String humanReadablePosition = request.getParameter(BARCODE_HUMAN_READABLE_POS);
        String pattern = request.getParameter(BARCODE_HUMAN_READABLE_PATTERN);
        String humanReadableSize = request.getParameter(BARCODE_HUMAN_READABLE_SIZE) != null ? request
            .getParameter(BARCODE_HUMAN_READABLE_SIZE) : "30pt";
        String humanReadableFont = request.getParameter(BARCODE_HUMAN_READABLE_FONT);
        if (!((humanReadablePosition == null) && (pattern == null) && (humanReadableSize == null) && (humanReadableFont == null))) {
            attr = new DefaultConfiguration("human-readable");
            DefaultConfiguration subAttr;
            if (pattern != null) {
                subAttr = new DefaultConfiguration("pattern");
                subAttr.setValue(pattern);
                attr.addChild(subAttr);
            }
            if (humanReadableSize != null) {
                subAttr = new DefaultConfiguration("font-size");
                subAttr.setValue(humanReadableSize);
                attr.addChild(subAttr);
            }
            if (humanReadableFont != null) {
                subAttr = new DefaultConfiguration("font-name");
                subAttr.setValue(humanReadableFont);
                attr.addChild(subAttr);
            }
            if (humanReadablePosition != null) {
                subAttr = new DefaultConfiguration("placement");
                subAttr.setValue(humanReadablePosition);
                attr.addChild(subAttr);
            }
            child.addChild(attr);
        }
        return cfg;
    }
}
